package org.stayfool.auth.common.dao;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

/**
 * Created by jych1 on 2016/3/9.
 */
@Repository("baseDao")
public class BaseDaoImpl implements BaseDao {

	@Autowired
	protected SessionFactory sessionFactory;

	protected static Object[] EMPTY_PARAM = new Object[0];

	public Serializable save(Object entity) {
		Assert.notNull(entity, "entity should not be null");
		return getSession().save(entity);
	}

	public void update(Object entity) {
		Assert.notNull(entity, "entity should not be null");
		getSession().update(entity);
	}

	public void delete(Object entity) {
		Assert.notNull(entity, "entity should not be null");
		getSession().delete(entity);
	}

	public <T> T get(Class<T> cls, String uuid) {
		Assert.notNull(cls, "class should not be null");
		Assert.hasText(uuid, "uuid should not be null");
		return getSession().get(cls, uuid);
	}

	public <T> Map<T, Serializable> saveAll(Collection<T> entities) {
		Assert.notNull(entities, "entities should not be null");
		Assert.notEmpty(entities, "entities should not be empty");
		Assert.noNullElements(entities.toArray(), "entities should not be empty");
		Map<T, Serializable> result = new HashMap<>();
		Session session = getSession();
		entities.stream().forEach(t -> result.put(t, session.save(t)));
		return result;
	}

	public void updateAll(Collection<Object> entities) {
		Assert.notNull(entities, "entities should not be null");
		Assert.notEmpty(entities, "entities should not be empty");
		Assert.noNullElements(entities.toArray(), "entities should not be empty");
		Session session = getSession();
		entities.stream().forEach(t -> session.update(t));
	}

	public void deleteAll(Collection<Object> entities) {
		Assert.notEmpty(entities, "entities should not be empty");
		Assert.noNullElements(entities.toArray(), "entities should not be empty");
		Session session = getSession();
		entities.stream().forEach(t -> session.delete(t));
	}

	public <T> List<T> find(String hql) {
		return find(hql, EMPTY_PARAM);
	}

	public <T> List<T> find(String hql, Object... params) {
		return find(hql, 0, Integer.MAX_VALUE, params);
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> find(String hql, int firstResult, int maxResult, Object... params) {
		Assert.hasText(hql, "hql should not be null");
		Query<T> query = getSession().createQuery(hql);
		query.setFirstResult(firstResult);
		query.setMaxResults(maxResult);
		if (params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, params[i]);
			}
		}
		return query.getResultList();
	}

	public void execute(String hql) {
		execute(hql, EMPTY_PARAM);
	}

	public void execute(String hql, Object... params) {
		Assert.hasText(hql, "hql should not be null");
		Query<?> query = getSession().createQuery(hql);
		if (params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, params[i]);
			}
		}
		query.executeUpdate();
	}

	public <T> T findUnique(String hql) {
		return findUnique(hql, EMPTY_PARAM);
	}

	@SuppressWarnings("unchecked")
	public <T> T findUnique(String hql, Object... params) {
		Assert.hasText(hql, "hql should not be null");
		Query<T> query = getSession().createQuery(hql);
		query.setFirstResult(0);
		query.setMaxResults(1);
		if (params != null && params.length > 0)
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, params[i]);
			}
		List<T> list = query.getResultList();
		if (list.isEmpty())
			return null;
		return list.get(0);
	}

	@SuppressWarnings("unchecked")
	public <T> T findUnique(Class<T> cls, String uniqueProperty, Object value) {
		Assert.notNull(cls, "cls should not be null");
		Assert.hasText(uniqueProperty, "uniqueProperty should not be null");
		String hql = "from ? where ? = ? ";
		Query<T> query = getSession().createQuery(hql);
		query.setParameter(0, cls.getSimpleName());
		query.setParameter(1, uniqueProperty);
		query.setParameter(2, value);
		query.setFirstResult(0);
		query.setMaxResults(1);

		List<T> rt = query.getResultList();
		if (rt.isEmpty())
			return null;
		else
			return rt.get(0);
	}

	public <T> List<T> findObj(String hql) {
		return findObj(hql, EMPTY_PARAM);
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> findObj(String hql, Object... params) {
		Assert.hasText(hql, "hql should not be null");
		Query<T> query = getSession().createQuery(hql);
		if (params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, params[i]);
			}
		}
		return query.getResultList();
	}

	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}
}
